fix: persist lazy-loaded CSS across route changes #14419
+23
−4
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fix: Preserve CSS Styles for Lazy-Loaded Components
Description:
This pull request addresses an issue where lazy-loaded components in React Router applications would lose their styles upon route changes. Previously, when navigating away from a route, or when a component was lazy-loaded and remained active in another route, its associated CSS chunks could be removed from the DOM. This resulted in the component losing its styling and appearing visually broken.
Specifically, this change resolves the scenario where a lazy-loaded component, sharing the same CSS chunk as a route, would lose its styles when navigating away from that route, even if the component itself still existed in the DOM. Before this change, the
keyedLinks
mechanism generated route-specificlink
tags, and when a route unmounted, these styles were removed, leading to the unstyled lazy-loaded component.With this modification, the
href
of a CSS or JavaScript link is now tracked in a persistentSet
calledpersistentHrefs
. If alink
tag'shref
has already been injected into the DOM (i.e., it exists inpersistentHrefs
), that link will not be rendered again. This approach ensures that lazy-loaded CSS continues to be available for components across route changes, preventing style loss.Benefits of this Change:
link
tags.Before/After Scenario:
This change is particularly important for large and modular React applications, where dynamic loading of components and styles is a common pattern.
Illustrative Scenario:
Consider the following common setup in a React Router application:
Before this change:
/route-a
.RouteA
mounts,SharedComponent
is rendered, and itsSharedComponent.module.css
is dynamically injected into the DOM as a<link>
tag./route-b
.RouteA
unmounts. The<link>
tag forSharedComponent.module.css
(which was implicitly associated withRouteA
's lifecycle) is removed from the DOM.RouteB
mounts and rendersSharedComponent
. Due to previoushref
processing and potential browser/framework deduplication, a new<link>
tag forSharedComponent.module.css
is not re-injected.SharedComponent
onRouteB
appears unstyled, leading to a broken UI.After this change:
/route-a
.RouteA
mounts,SharedComponent
is rendered, and itsSharedComponent.module.css
is injected into the DOM as a<link>
tag. Thehref
forSharedComponent.module.css
is added to thepersistentHrefs
Set
./route-b
.RouteA
unmounts. The<link>
tag forSharedComponent.module.css
remains in the DOM because itshref
is now persistently tracked and not subject to removal by theLinks
component's filtering logic.RouteB
mounts and rendersSharedComponent
. TheLinks
component processesSharedComponent.module.css
, but because itshref
is already present inpersistentHrefs
, it skips re-injecting a duplicate<link>
tag.SharedComponent
onRouteB
retains its styles because the original<link>
tag continues to persist in the document.This ensures that once a CSS module for a shared or lazy-loaded component is loaded, its styles remain active throughout the application's single-page application (SPA) lifecycle, regardless of dynamic route changes.
Related Issues/Discussions: